import { $set } from '@feng3d/serialization';
import { ArrayCamera3D, CylinderGeometry3D, Mesh3D, MeshPhongMaterial3D, Node3D, PerspectiveCamera3D, PlaneGeometry3D, Scene3D, Vector4, WebGLRenderer3D } from 'feng3d-three';

let camera: ArrayCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;
let mesh: Mesh3D;
const AMOUNT = 6;

init();
animate();

function init()
{
    const ASPECT_RATIO = window.innerWidth / window.innerHeight;

    const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio;
    const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio;

    const cameras: PerspectiveCamera3D[] = [];

    for (let y = 0; y < AMOUNT; y++)
    {
        for (let x = 0; x < AMOUNT; x++)
        {
            const subcamera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 40, aspect: ASPECT_RATIO, near: 0.1, far: 10 });
            subcamera.viewport = new Vector4(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT));
            subcamera.entity.position.x = (x / AMOUNT) - 0.5;
            subcamera.entity.position.y = 0.5 - (y / AMOUNT);
            subcamera.entity.position.z = 1.5;
            subcamera.entity.position.multiplyScalar(2);
            subcamera.entity.lookAt(0, 0, 0);
            subcamera.updateMatrixWorld();
            cameras.push(subcamera);
        }
    }

    camera = new Node3D().addComponent('ArrayCamera3D', { cameras });
    camera.entity.position.z = 3;

    scene = new Node3D().addComponent('Scene3D');

    const ambientLight = new Node3D().addComponent('AmbientLight3D', { color: { value: 0x222244 } });
    scene.entity.addChild(ambientLight.entity);

    const light = new Node3D().addComponent('DirectionalLight3D');
    light.entity.position.set(0.5, 0.5, 1);
    light.castShadow = true;
    light.shadow.camera.zoom = 4; // tighter shadow map
    scene.entity.addChild(light.entity);

    const geometryBackground = $set(new PlaneGeometry3D(), { width: 100, height: 100 });
    const materialBackground = $set(new MeshPhongMaterial3D(), { color: { value: 0x000066 } });

    const background = new Node3D().addComponent('Mesh3D', {
        geometry: geometryBackground,
        material: materialBackground,
    });
    background.receiveShadow = true;
    background.entity.position.set(0, 0, -1);
    scene.entity.addChild(background.entity);

    const geometryCylinder = $set(new CylinderGeometry3D(), { radiusTop: 0.5, radiusBottom: 0.5, height: 1, radialSegments: 32 });
    const materialCylinder = $set(new MeshPhongMaterial3D(), { color: { value: 0xff0000 } });

    mesh = new Node3D().addComponent('Mesh3D', {
        geometry: geometryCylinder,
        material: materialCylinder,
    });
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    scene.entity.addChild(mesh.entity);

    renderer = new Node3D().addComponent('WebGLRenderer3D');
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    document.body.appendChild(renderer.domElement);

    //

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    const ASPECT_RATIO = window.innerWidth / window.innerHeight;
    const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio;
    const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio;

    camera.aspect = ASPECT_RATIO;
    camera.updateProjectionMatrix();

    for (let y = 0; y < AMOUNT; y++)
    {
        for (let x = 0; x < AMOUNT; x++)
        {
            const subcamera = camera.cameras[AMOUNT * y + x];

            subcamera.viewport.set(
                Math.floor(x * WIDTH),
                Math.floor(y * HEIGHT),
                Math.ceil(WIDTH),
                Math.ceil(HEIGHT));

            subcamera.aspect = ASPECT_RATIO;
            subcamera.updateProjectionMatrix();
        }
    }

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate()
{
    mesh.entity.rotation.x += 0.005;
    mesh.entity.rotation.z += 0.01;

    renderer.render(scene, camera);

    requestAnimationFrame(animate);
}
